#ifndef MAP_DISPLAY_H_
#define MAP_DISPLAY_H_

#include <allegro.h>
#include <stdlib.h>
#include <stdint.h>
#include "util/rect.h"
#include "world/map.h"
#include "world/actor.h"
#include "world/effect.h"

#define P_OFFSET (TILE_SIZE/4) // perspective offset

#define MAX_EFFECTS 100

#define SUB_RES 2 // light resolution 
#define SUBTILES (SUB_RES*SUB_RES) // 1 tile contains SUBTILES light tiles
#define LTILE_SIZE (TILE_SIZE/SUB_RES) // Light tile wh in pixels
#define LIGHT_REACH (10 * SUB_RES)    // from this distance on, nothing is visible
#define FULLLIGHT_REACH (7 * SUB_RES) // 100% light will go this many tiles far
#define LIGHT_PER_TILE (255.0 / (LIGHT_REACH - FULLLIGHT_REACH))
#define LIGHTTABLE_WH (2*LIGHT_REACH+1) // WH of a single light table
#define LIGHTTABLE_SIZE (LIGHTTABLE_WH * LIGHTTABLE_WH * sizeof(uint8_t)) // byte size
#define LIGHTMEMPOOL_SIZE (4 * 1024 * 1024) // Nr of bytes for light table memory

/*
 * A LightTable is a collection of darkness values centered around a
 * square of the map. <where> is at the center of the light in
 * this table, and the darkness of all other squares represented are
 * calculated from the center square using a flood-fill algorithm that
 * will not go through obstacles like walls. The amount of squares or
 * locations in the LightTable are such that in an area where there are
 * no obstacles in any direction, the LightTable is just big enough to
 * encompass all squares in the outer light radius. Any squares outside
 * the LightTable thus have a darkness value of 255 (maximum).
 * The area represented by one LightTable is LIGHTTABLE_WH*LIGHTTABLE_WH
 * tiles in size, and SUB_RES*SUB_RES LightTable'd tiles
 * fit exactly into a single tile on the actual map. To get the light value
 * for a location that is not directly represented in the light table,
 * an interpolation function is used.
 * To get the darkness value of a tile located at c, with components in 
 * light tile units (not map tiles!), from a LightTable "tbl" use:
 *   tbl->value[(c.y - tbl->where.y + LIGHTTABLE_WH/2)*LIGHTTABLE_WH +
 *              (c.x - tbl->where.x + LIGHTTABLE_WH/2)]
 */
typedef struct {
	Coord where; // Coordinates of the tile central to this table
	uint8_t* value; // Darkness value array. Values 0 (bright) to 255 (black)
} LightTable;

/*
 * Contains all the darkness data for all the tiles in a Map.
 * Light tables are generated on-demand and discarded FIFO if 
 * we are short on memory.
 */
typedef struct {
	LightTable* mem; // Light table memory that is available
	int capacity;    // How many light tables in mem
	int next;        // Next light table index (head of queue)
	LightTable** table; // Contains pointers into mem or NULL, in reach
} Light;

/*
 * A MapDisplay contains all drawing and graphics related information
 * about a Map.
 */
typedef struct {
	Map* map;
	BITMAP** tiles; // usage: blit(map->tiles[zlayer], ...)
	BITMAP* objects;
	
	Effect effect [MAX_EFFECTS];
	int next_effect; // for inserting new effects
	int anim_frame; // for animated tiles like teleporters
	
	Light light; // Memory pool for lighting
	
	Coord observer; // map coords in pixels of observer
	int offset_x;   // pixel offset for drawing stuff to the screen
	int offset_y;
} MapDisplay;

/*
 * Initializes the MapDisplay. Also chooses graphics for all map tiles.
 * Requires Allegro to be initialized.
 */
void map_display_init (MapDisplay* d, Map* m);

/*
 * Free resources
 */
void map_display_cleanup (MapDisplay* d); 

/*
 * Changes the viewpoint from which the map is seen.
 * This influences mostly light levels and scrolling.
 */
void map_display_set_observer (MapDisplay* display, Coord observer);

/*
 * TODO: FUTURE DIRECTIONS: Drawing takes place in 6 passes:
 *       tile bg, effects bg, objects, actors, tile fg, effects fg
 * Drawing takes place in two passes: map_display_bg draws everything
 * behind the actors, map_display_fg draws everything in front of the
 * actors. fg includes decorative graphics to make the player disappear
 * behind some walls. Effects are split between bg and fg. They are 
 * drawn in fg by default, except if effect.is_background, in which case 
 * the part of the effect that overlaps with its tile of origin 
 * (effect.where) is drawn in bg, everything else in fg.
 * target is the bitmap where the map will be drawn all over, like the
 * screen buffer. player is the position of the player in pixels where
 * (TILE_SIZE/2,TILE_SIZE/2) means that the player is standing in the
 * middle of the upper left corner tile of the map. player.z is layers.
 */
void map_display_bg (MapDisplay* d, BITMAP* target);
void map_display_objects (MapDisplay* d, BITMAP* target);
void map_display_character (MapDisplay* display, Actor* p);
void map_display_monster (MapDisplay* display, Actor* m);
void map_display_fg (MapDisplay* d, BITMAP* target);

/*
 * Insert effect to be displayed
 */
void map_display_add_effect (MapDisplay* m, Effect e);

/* 
 * Computes, by interpolation, the darkness value for any pixel location
 * on the map. As basis for the interpolation, the darkness values from
 * the nearest (up to) four light-tiles surrounding <target>, as 
 * observed from the nearest (up to) four light-tiles surrounding the
 * observer location are taken into consideration in proportion to 
 * their distance from the precise pixel coordinates. Thus, 16 darkness
 * values are merged into one to provide the smoothest shadow
 * progression especially when the observer is in motion.
 * 
 * <target> in absolute map pixels.
 */
uint8_t map_display_get_darkness (MapDisplay* d, Coord target);

/*
 * Returns the pixel-precise coordinates of the actor
 * on the map (not the screen!). These coordinates point
 * to the center of the tile where the actor is currently
 * standing, or if they are moving between the tiles, 
 * adjusted accordingly.
 */
Coord actor_pxpos (Actor *actor);

#endif /* MAP_DISPLAY_H_ */
